package com.mocchira.dq2011.sp;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class SlidePuzzleInputParser {
    public static class Header {
        public int lx;
        public int rx;
        public int ux;
        public int dx;
        public int total;
        public String toString() {
            return "lx:" + lx + " rx:" + rx + " ux:" + ux + " dx:" + dx + " total:" + total + System.getProperty("line.separator");
        }
    }
    public static class Puzzle {
        public static final char WALL  = '=';
        public static final char BLANK = '0';
        public int numWall = 0;
        public int width = 0;
        public int height = 0;
        public char[] boards = null;
        private char[] resultBoards = null;
        protected boolean isLegalBoards() {
            int wnum = 0;
            int bnum = 0;
            if (resultBoards == null) {
                resultBoards = new char[boards.length];
            }
            for (int i = 0; i < boards.length; i++) {
                char c = boards[i];
                int idx = -1;
                if (c == WALL) {
                    wnum++;
                }else if (c == BLANK) {
                    resultBoards[boards.length - 1] = c;
                    bnum++;
                } else {
                    if (c >= '1' && c <= '9')
                        idx = c - '1';
                    else if (c >= 'A' && c <= 'Z')
                        idx = c - 'A' + 9;
                    else
                        return false;
                    try {
                        resultBoards[idx] = c;
                    } catch (ArrayIndexOutOfBoundsException ex) {
                        return false;
                    }
                }
            }
            if (bnum != 1) return false;
            if (wnum > 0) {
                numWall = wnum;
                for (int i = 0; i < resultBoards.length; i++) {
                    if (resultBoards[i] == '\u0000') {
                        if (wnum > 0)
                            resultBoards[i] = WALL;
                        else
                            return false;
                        wnum--;
                    }
                }
            } else {
                for (int i = 0; i < resultBoards.length; i++) {
                    if (resultBoards[i] == '\u0000')
                        return false;
                }
            }
            return true;
        }
        public boolean isLegalFormat() {
            if (width == 0 || height == 0 || boards == null) return false;
            int num = width * height;
            if (num != boards.length) return false;
            if (!isLegalBoards()) return false;
            return true;
        }
        private boolean match(final char[] src, final char[] dst) {
            if (src == null || src.length != dst.length)
                return false;
            for (int i = 0; i < dst.length; i++) {
                if (dst[i] != src[i])
                    return false;
            }
            return true;
        }
        public boolean matchStartPos(final char[] src) {
            return match(src, boards);
        }
        public boolean matchResultPos(final char[] src) {
            return match(src, resultBoards);
        }
        public final char[] getResultBoards() {
            return resultBoards;
        }
        public String toString() {
            StringBuilder dst = new StringBuilder("width:" + width + " height:" + height + System.getProperty("line.separator"));
            dst.append("boards:" + String.valueOf(boards) + System.getProperty("line.separator"));
            dst.append("result:" + String.valueOf(resultBoards) + System.getProperty("line.separator"));
            return dst.toString();
        }
    }
    public static class Input {
        public Header header = new Header();
        public List<Puzzle> listPuzzle = new ArrayList<Puzzle>();
        public String toString() {
            StringBuilder dst = new StringBuilder();
            dst.append(header);
            for (Puzzle puzzle: listPuzzle) {
                dst.append(puzzle);
            }
            return dst.toString();
        }
    }
    
    private static void skip(int lineNo, String line) {
        System.out.println("no[" + lineNo + "] line[" + line + "] skipped.");
    }
    
    public static Input parse(File file) {
        if (file == null || !file.canRead())
            return null;
        int lineNo = 1;
        BufferedReader reader = null;
        Input input = new Input();
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
            String line;
            while ((line =reader.readLine()) != null) {
                try {
                    if (lineNo == 1) {
                        String[] totals = line.split(" ");
                        if (totals == null || totals.length != 4)
                            throw new IllegalArgumentException("line1 parse error");
                        input.header.lx = Integer.parseInt(totals[0]);
                        input.header.rx = Integer.parseInt(totals[1]);
                        input.header.ux = Integer.parseInt(totals[2]);
                        input.header.dx = Integer.parseInt(totals[3]);
                    } else if (lineNo == 2) {
                        input.header.total = Integer.parseInt(line);
                    } else {
                        String[] puzzleInfo = line.split(",");
                        if (puzzleInfo == null || puzzleInfo.length != 3)
                            throw new IllegalArgumentException("The quiz line[" + lineNo + "] parse error");
                        Puzzle puzzle = new Puzzle();
                        puzzle.width  = Integer.parseInt(puzzleInfo[0]);
                        puzzle.height = Integer.parseInt(puzzleInfo[1]);
                        puzzle.boards = puzzleInfo[2].toCharArray();
                        if (!puzzle.isLegalFormat()) {
                            throw new IllegalArgumentException("The quiz line[" + lineNo + "] parse error");
                        }
                        input.listPuzzle.add(puzzle);
                    }
                } catch (Exception ex) {
                    System.err.println(ex.getMessage());
                    skip(lineNo, line);
                }
                lineNo++;
            }
            return input;
        } catch (IOException iex) {
            System.err.println(iex.getMessage());
            return null;
        } finally {
            if (reader != null) try {reader.close();} catch (Exception ex){;}
        }
    }
}
